#访问者模式

#一、访问者模式的优点和应用场景
#优点：
#1、将不同的职责非常明确地分离开来，符合单一职责原则；2、职责的分开也直接导致扩展非常优良，灵活性非常高，加减元素和访问者都非常容易。

#应用场景：
#1、要遍历不同的对象，根据对象进行不同的操作的场景；或者一个对象被多个不同对象顺次处理的情况，可以考虑使用访问者模式。除本例外，报表生成器也可以使用访问者模式实现，报表的数据源由多个不同的对象提供，每个对象都是Visitor，报表这个Element顺次Accept各访问者完善并生成对象。

#二、访问者模式的缺点
#1、访问者得知了元素细节，与最小隔离原则相悖；2、元素变更依旧可能引起Visitor的修改。

class Medicine:
    name = ""
    price = 0.0

    def __init__(self, name, price):
        self.name = name
        self.price = price

    def getName(self):
        return self.name

    def setName(self, name):
        self.name = name

    def getPrice(self):
        return self.price

    def setPrice(self, price):
        self.price = price

    def accept(self, visitor):
        pass


class Antibiotic(Medicine):
    def accept(self, visitor):
        visitor.visit(self)


class Coldrex(Medicine):
    def accept(self, visitor):
        visitor.visit(self)


class Visitor:
    name = ""

    def setName(self, name):
        self.name = name

    def visit(self, medicine):
        pass


class Charger(Visitor):
    def visit(self, medicine):
        print ("CHARGE: %s lists the Medicine %s. Price:%s " % (self.name, medicine.getName(), medicine.getPrice()))



class Pharmacy(Visitor):
    def visit(self, medicine):
        print ("PHARMACY:%s offers the Medicine %s. Price:%s" % (self.name, medicine.getName(), medicine.getPrice()))


class ObjectStructure:
    pass


class Prescription(ObjectStructure):
    medicines = []

    def addMedicine(self, medicine):
        self.medicines.append(medicine)

    def rmvMedicine(self, medicine):
        self.medicines.append(medicine)

    def visit(self, visitor):
        for medc in self.medicines:
            medc.accept(visitor)


if __name__ == "__main__":
    yinqiao_pill = Coldrex("Yinqiao Pill", 2.0)
    penicillin = Antibiotic("Penicillin", 3.0)
    doctor_prsrp = Prescription()
    doctor_prsrp.addMedicine(yinqiao_pill)
    doctor_prsrp.addMedicine(penicillin)
    charger = Charger()
    charger.setName("Doctor Strange")
    pharmacy = Pharmacy()
    pharmacy.setName("Doctor Wei")
    doctor_prsrp.visit(charger)
    doctor_prsrp.visit(pharmacy)

